package fr.vvins.core.services;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.ws.rs.Path;
import javax.ws.rs.core.PathSegment;

import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import fr.vvins.core.biz.exception.BizException;
import fr.vvins.core.biz.search.SearchException;
import fr.vvins.core.biz.search.SearchManager;
import fr.vvins.core.biz.search.dto.SearchFacetResultDto;
import fr.vvins.core.biz.search.dto.SearchResultDto;
import fr.vvins.core.biz.search.dto.SuggestResultDto;
import fr.vvins.core.common.TechnicalException;

@Path("/search")
@Component("searchService")
public class SearchServiceImpl implements SearchService {
	@Resource(name="searchManager")
	public SearchManager manager;
	@Resource(name="defaultFacets")
	public List<String> defaultFacets;
	@Resource(name="defaultFacetsRange")
	public Map<String, Map<String,Long>> defaultFacetsRange;

	@Override
	public SuggestResultDto suggest(String word) throws TechnicalException, BizException {
		
		if(!StringUtils.hasLength(word)){
			throw new SearchException(SearchException.EMPTY_PARAMETER,"Le parametre 'word' est vide");
		}
		return getManager().suggest(decode(word));
	}
	
	@Override
	public SuggestResultDto suggestEntity(String entity, String word) throws TechnicalException, BizException {
		if(!StringUtils.hasLength(entity)){
			throw new SearchException(SearchException.EMPTY_PARAMETER,"Le parametre 'entity' est vide");
		}
		if(!StringUtils.hasLength(word)){
			throw new SearchException(SearchException.EMPTY_PARAMETER,"Le parametre 'word' est vide");
		}
		return getManager().suggestEntity(entity, decode(word));
	}
	
	@Override
	public SearchResultDto find(String word) throws TechnicalException, BizException {
		if(!StringUtils.hasLength(word)){
			throw new SearchException(SearchException.EMPTY_PARAMETER,"Le parametre 'word' est vide");
		}
		return getManager().find(decode(word));
	}
	
	
	@Override
	public SearchFacetResultDto find(String word, String facets, List<PathSegment> facetsLimit, String facetsRange) throws TechnicalException, BizException {
		if(!StringUtils.hasLength(word)){
			throw new SearchException(SearchException.EMPTY_PARAMETER,"Le parametre 'word' est vide");
		}
		return manager.find(decode(word), parseFacets(decode(facets)), parseFacetsLimit(facetsLimit), parseFacetsRange(decode(facetsRange)));
	}

	@Override
	public SearchFacetResultDto find(String facets, List<PathSegment> facetsLimit, String facetsRange) throws TechnicalException, BizException {
		
		if(facetsLimit == null || facetsLimit.isEmpty()){
			throw new SearchException(SearchException.EMPTY_PARAMETER,"Les parametres 'facetsLimit' sont vide");
		}	
		return manager.find(parseFacets(decode(facets)), parseFacetsLimit(facetsLimit), parseFacetsRange(decode(facetsRange)));
	}
	
	@Override
	public SearchResultDto findEntity(String entity, String word) throws TechnicalException, BizException {
		if(!StringUtils.hasLength(entity)){
			throw new SearchException(SearchException.EMPTY_PARAMETER,"Le parametre 'entity' est vide");
		}
		if(!StringUtils.hasLength(word)){
			throw new SearchException(SearchException.EMPTY_PARAMETER,"Le parametre 'word' est vide");
		}
		return getManager().findEntity(entity, decode(word));
	}
	
	
	@Override
	public SearchFacetResultDto findEntity(String entity, String word, String facets, List<PathSegment> facetsLimit, String facetsRange) throws TechnicalException, BizException {

		if(!StringUtils.hasLength(word)){
			throw new SearchException(SearchException.EMPTY_PARAMETER,"Le parametre 'word' est vide");
		}
		return manager.findEntity(entity, decode(word), parseFacets(decode(facets)), parseFacetsLimit(facetsLimit), parseFacetsRange(decode(facetsRange)));
		
	}

	@Override
	public SearchFacetResultDto findEntity(String entity, String facets, List<PathSegment> facetsLimit, String facetsRange) throws TechnicalException, BizException {
		
		if(facetsLimit == null || facetsLimit.isEmpty()){
			throw new SearchException(SearchException.EMPTY_PARAMETER,"Les parametres 'facets' sont vide");
		}	
		return manager.findEntity(entity, parseFacets(facets), parseFacetsLimit(facetsLimit), parseFacetsRange(facetsRange));
	}
	
	private Map<String,String> parseFacetsLimit(List<PathSegment> paths){
		Map<String,String> facets = new HashMap<String,String>();
		if(paths != null && paths.size() > 0) {
			for(PathSegment facet : paths){
				if("all".equals(facet.getPath())){
					return new HashMap<String,String>();
				} else if(facet.getPath().contains(":")){
					String[] splits = facet.getPath().split(":");
					if(splits.length >=2 ) {
						facets.put(splits[0], splits[1]);
					}
				}	
			}
		}
		return facets;
	}
	
	private Map<String,Map<String,Long>> parseFacetsRange(String facetsRange){
		if(!StringUtils.hasText(facetsRange)){
			return defaultFacetsRange;
		}
		Map<String,Map<String,Long>> facetsRangeMap = new HashMap<String,Map<String,Long>>();
		String[] rangeFields = facetsRange.split(",");
		for(String rangeField : rangeFields){
			String[] rangeFieldValue = rangeField.split(":");
			Map<String,Long> facetsRangeMapEntry = new HashMap<String, Long>();
			if(rangeFieldValue.length > 1){
				facetsRangeMapEntry.put("start", Long.parseLong(rangeFieldValue[1]));
			}
			if(rangeFieldValue.length > 2){
				facetsRangeMapEntry.put("end", Long.parseLong(rangeFieldValue[2]));
			}
			if(rangeFieldValue.length > 3){
				facetsRangeMapEntry.put("gap", Long.parseLong(rangeFieldValue[3]));
			}
		}
		return facetsRangeMap;
	}
	
	private List<String> parseFacets(String facets){
		if(!StringUtils.hasText(facets)){
			return defaultFacets;
		}
		return Arrays.asList(facets.split(","));
	}
	
	private String decode(String value) throws TechnicalException{
		try {
			if(value != null) {
				return URLDecoder.decode(value,"utf-8");
			}
			return null;
		} catch (UnsupportedEncodingException e) {
			throw new TechnicalException(TechnicalException.ENCODING, "This encoding is not supported : " + "utf-8");
		}
	}


	public SearchManager getManager() {
		return manager;
	}

	public void setManager(SearchManager manager) {
		this.manager = manager;
	}

	public List<String> getDefaultFacets() {
		return defaultFacets;
	}

	public void setDefaultFacets(List<String> defaultFacets) {
		this.defaultFacets = defaultFacets;
	}

	public Map<String, Map<String, Long>> getDefaultFacetsRange() {
		return defaultFacetsRange;
	}

	public void setDefaultFacetsRange(
			Map<String, Map<String, Long>> defaultFacetsRange) {
		this.defaultFacetsRange = defaultFacetsRange;
	}
}
